[NPCCTF 2025]babyVM

史vm题

ida打开main函数看到进入就是vm虚拟机

点进去发现是一个巨大的switch case循环,结构非常明显,有四个32位寄存器,其他和题目关系不大

二十多个case全部写handle去解析opcode的话难度很大,而且还要指令长度之类的问题,可以采用在指令入口点下条件断点的方式,然后动态执行整个vm,在执行指令的同时输出执行的操作,这样就可以获取vm指令的伪代码了


然后跑一遍就能拿到执行的指令了

可以看到非常长,这里我同时在指令前输出了执行前寄存器的值,同时这里的下标也是处理过的,如果直接输出寄存器的值下标会非常大,用了一个python脚本对下标做了偏移让伪代码好看点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import re
import sys


def process_text(text):
# 找出所有以 heap[938 开头的模式
pattern1 = r'heap\[(938\d+)\]'
matches1 = re.findall(pattern1, text)

# 找出所有以 heap[1844 开头的模式
pattern2 = r'heap\[(1844\d+)\]'
matches2 = re.findall(pattern2, text)

# 如果存在匹配项,找出最小索引值
if matches1:
indices1 = [int(idx) for idx in matches1]
base1 = min(indices1)

# 替换heap[938...]为heap_1[offset]
for idx in sorted(set(indices1), reverse=True): # 去重并从大到小替换
offset = idx - base1
text = text.replace(f'heap[{idx}]', f'heap_1[{offset}]')

if matches2:
indices2 = [int(idx) for idx in matches2]
base2 = min(indices2)

# 替换heap[1844...]为heap_2[offset]
for idx in sorted(set(indices2), reverse=True): # 去重并从大到小替换
offset = idx - base2
text = text.replace(f'heap[{idx}]', f'heap_2[{offset}]')

return text


def main():
# 如果有命令行参数,则读取文件
if len(sys.argv) > 1:
input_file = sys.argv[1]
with open(input_file, 'r') as f:
text = f.read()
else:
# 否则从标准输入读取
text = sys.stdin.read()

processed_text = process_text(text)

# 输出到标准输出
print(processed_text)


if __name__ == "__main__":
main()

然后看cmp xx 48可以看出来明显有一个循环,结合各种左右移操作,猜测是tea系列的加密,于是截取一段循环抄算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// ------------cmp 4  48-------------
// r1= 2984982102 r2= 0 r3= 4 r4= 4207947599
// r1= 2984982102 r2= 0 r3= 4 r4= 4207947599
reg3 = heap_2[56]; // heap_2[56] = 2984982102
// r1= 2984982102 r2= 0 r3= 4 r4= 2984982102
reg3 >>= 6;
// r1= 2984982102 r2= 0 r3= 4 r4= 46640345
reg0 = heap_2[56]; // heap_2[56] = 2984982102
// r1= 2984982102 r2= 0 r3= 4 r4= 46640345
reg0 <<= 5;
// r1= 1030146752 r2= 0 r3= 4 r4= 46640345
reg3 ^= reg0; // reg0 = 1030146752
// r1= 1030146752 r2= 0 r3= 4 r4= 1067542041
reg3 += heap_2[56]; // heap_2[56] = 2984982102
// r1= 1030146752 r2= 0 r3= 4 r4= 4052524143
reg2 = heap_2[64]; // heap_2[64] = 2610559960
// r1= 1030146752 r2= 0 r3= 2610559960 r4= 4052524143
reg2 &= 3;
// r1= 1030146752 r2= 0 r3= 0 r4= 4052524143
reg0 = heap_2[68]; // heap_2[68] = 164
// r1= 164 r2= 0 r3= 0 r4= 4052524143
reg2 = heap_1[0]; // heap_1[0] = 1987012675
// r1= 164 r2= 0 r3= 1987012675 r4= 4052524143
reg2 += heap_2[64]; // heap_2[64] = 2610559960
// r1= 164 r2= 0 r3= 302605339 r4= 4052524143
reg3 ^= reg2; // reg2 = 302605339
// r1= 164 r2= 0 r3= 302605339 r4= 3817207924
reg3 += heap_2[60]; // heap_2[60] = 1830509707
// r1= 164 r2= 0 r3= 302605339 r4= 1352750335
heap_2[60] = 1352750335;
// r1= 164 r2= 0 r3= 302605339 r4= 1352750335
reg3 = heap_2[64]; // heap_2[64] = 2610559960
// r1= 164 r2= 0 r3= 302605339 r4= 2610559960
reg3 -= heap_2[32]; // heap_2[32] = 421101834
// r1= 164 r2= 0 r3= 302605339 r4= 2189458126
heap_2[64] = 2189458126;
// r1= 164 r2= 0 r3= 302605339 r4= 2189458126
reg0 = heap_2[60]; // heap_2[60] = 1352750335
// r1= 1352750335 r2= 0 r3= 302605339 r4= 2189458126
reg0 >>= 7;
// r1= 10568361 r2= 0 r3= 302605339 r4= 2189458126
reg2 = heap_2[60]; // heap_2[60] = 1352750335
// r1= 10568361 r2= 0 r3= 1352750335 r4= 2189458126
reg2 <<= 3;
// r1= 10568361 r2= 0 r3= 2232068088 r4= 2189458126
reg0 ^= reg2; // reg2 = 2232068088
// r1= 2242635089 r2= 0 r3= 2232068088 r4= 2189458126
reg0 += heap_2[60]; // heap_2[60] = 1352750335
// r1= 3595385424 r2= 0 r3= 2232068088 r4= 2189458126
reg3 = heap_2[64]; // heap_2[64] = 2189458126
// r1= 3595385424 r2= 0 r3= 2232068088 r4= 2189458126
reg3 >>= 11;
// r1= 3595385424 r2= 0 r3= 2232068088 r4= 1069071
reg3 &= 3;
// r1= 3595385424 r2= 0 r3= 2232068088 r4= 3
reg2 = heap_2[68]; // heap_2[68] = 164
// r1= 3595385424 r2= 0 r3= 164 r4= 3
reg3 = heap_1[12]; // heap_1[12] = 1597387639
// r1= 3595385424 r2= 0 r3= 164 r4= 1597387639
reg3 += heap_2[64]; // heap_2[64] = 2189458126
// r1= 3595385424 r2= 0 r3= 164 r4= 3786845765
reg0 ^= reg3; // reg3 = 3786845765
// r1= 939234325 r2= 0 r3= 164 r4= 3786845765
reg0 += heap_2[56]; // heap_2[56] = 2984982102
// r1= 3924216427 r2= 0 r3= 164 r4= 3786845765
heap_2[56] = 3924216427;
// r1= 3924216427 r2= 0 r3= 164 r4= 3786845765
// r1= 3924216427 r2= 0 r3= 164 r4= 3786845765
reg2 = heap_2[44]; // heap_2[44] = 4
// r1= 3924216427 r2= 0 r3= 4 r4= 3786845765
reg2 += 1;
// r1= 3924216427 r2= 0 r3= 5 r4= 3786845765
heap_2[44] = 5;
// r1= 3924216427 r2= 0 r3= 5 r4= 3786845765
// ------------cmp 5 48-------------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// key = Chovy_inkey_w36_
void enc(unsigned int v0, unsigned int v1, unsigned int *key)
{
// heap_2[56] = v0
// heap_2[60] = v1
// heap_2[64] = sum
unsigned int sum = 0;
unsigned int tmp_reg3 = 0, tmp_reg2 = 0, tmp_reg0 = 0;
for (int i = 0; i < 48; i++)
{
v0 += (((v1 >> 6) ^ (v1 << 5) )+ v1) ^ (key[sum & 3] + sum);
sum -= 421101834;
v1 += (((v0 >> 7) ^ (v0 << 3)) + v0) ^ (key[(sum >> 11) & 3] + sum);
}
}

key 的来源是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
heap_2[0] = 67;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[1] = 104;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[2] = 111;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[3] = 118;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[4] = 121;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[5] = 95;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[6] = 105;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[7] = 110;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[8] = 107;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[9] = 101;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[10] = 121;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[11] = 95;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[12] = 119;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[13] = 51;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[14] = 54;
// r1= 512 r2= 0 r3= 32 r4= 512
heap_2[15] = 95;

其中可能在整理heap时脚本出了点问题,储存key的被错误命名成了heap2,但是还是能辨别出key

于是写解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void dec(unsigned int *v, unsigned int *key)
{
unsigned int v0 = v[0];
unsigned int v1 = v[1];
unsigned int delta = 421101834;
unsigned int sum = 0;
for (int i = 0; i < 48; i++)
sum -= delta;
for (int i = 0; i < 48; i++)
{
v1 -= (((v0 >> 7) ^ (v0 << 3)) + v0) ^ (key[(sum >> 11) & 3] + sum);
sum += delta;
v0 -= (((v1 >> 6) ^ (v1 << 5)) + v1) ^ (key[sum & 3] + sum);
}
for (int i = 0; i < 4; i++)
printf("%c", ((unsigned char *)&v0)[i]);
for (int i = 0; i < 4; i++)
printf("%c", ((unsigned char *)&v1)[i]);
}
int main()
{
unsigned char key[] = "Chovy_inkey_w36_";
unsigned char cipher[] = "\xE5\xDF\xF0\xA1\xF4\xBD\x6A\xDB\x1B\xE9\xDD\x20\r\x9D!YгY)\xB9\xEC\x2F\xC0\"~\xAD\xE1\xB0\x15\xB6)";
for (int i = 0; i < 32; i += 8)
dec((unsigned int *)&cipher[i], (unsigned int *)key);
return 0;
}

flag{D0_yOu_l1k3_VmmmmMMMMMmmm?}

Author

SGSG

Posted on

2025-03-31

Updated on

2025-04-18

Licensed under